<template>
  <!-- three 波浪粒子 -->
  <div class="wrap">
    <div id="threeParticle"></div>
  </div>
</template>
 
<script setup>
import {  ref, onMounted } from 'vue'
import * as THREE from "three";

const props = defineProps({
  amountX: {
    type: Number,
    default: 50,
  },
  amountY: {
    type: Number,
    default: 50,
  },
  color: {
    type: Number,
    default: 0x497fb7,
  },
  top: {
    type: Number,
    default: 0,
  }
})

const particle = {
  count: 0,
  // 用来跟踪鼠标水平位置
  mouseX: 0,
  windowHalfX: null,
  // 相机
  camera: null,
  // // 场景
  scene: null,
  // 批量管理粒子
  particles: null,
  // 渲染器
  renderer: null,
};

onMounted(() => {
  init();
  animate();
  // onWindowResize();
})

function init() {
  const SEPARATION = 200;
  const SCREEN_WIDTH = window.innerWidth;
  const SCREEN_HEIGHT = window.innerHeight;
  const container = document.createElement("div");
  particle.windowHalfX = window.innerWidth / 2;
  container.style.position = "relative";
  container.style.top = `${props.top}px`;
  container.style.height = `${SCREEN_HEIGHT - props.top}px`;
  document.getElementById("threeParticle").appendChild(container);

  particle.camera = new THREE.PerspectiveCamera(
    75,
    SCREEN_WIDTH / SCREEN_HEIGHT,
    1,
    10000
  );
  particle.camera.position.z = 1000;

  particle.scene = new THREE.Scene();

  const numParticles = props.amountX * props.amountY;
  const positions = new Float32Array(numParticles * 3);
  const scales = new Float32Array(numParticles);
  // 初始化粒子位置和大小
  let i = 0;
  let j = 0;
  for (let ix = 0; ix < props.amountX; ix++) {
    for (let iy = 0; iy < props.amountY; iy++) {
      positions[i] = ix * SEPARATION - (props.amountX * SEPARATION) / 2;
      positions[i + 1] = 0;
      positions[i + 2] = iy * SEPARATION - (props.amountY * SEPARATION) / 2;
      scales[j] = 1;
      i += 3;
      j++;
    }
  }

  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute(
    "position",
    new THREE.BufferAttribute(positions, 3)
  );
  geometry.setAttribute("scale", new THREE.BufferAttribute(scales, 1));
  // 初始化粒子材质
  const material = new THREE.ShaderMaterial({
    uniforms: {
      color: { value: new THREE.Color(props.color) },
    },
    vertexShader: `
          attribute float scale;
          void main() {
            vec4 mvPosition = modelViewMatrix * vec4( position, 2.0 );
            gl_PointSize = scale * ( 300.0 / - mvPosition.z );
            gl_Position = projectionMatrix * mvPosition;
          }
        `,
    fragmentShader: `
          uniform vec3 color;
          void main() {
            if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;
            gl_FragColor = vec4( color, 1.0 );
          }
        `,
  });

  particle.particles = new THREE.Points(geometry, material);
  particle.scene.add(particle.particles);

  particle.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
  particle.renderer.setSize(container.clientWidth, container.clientHeight);
  particle.renderer.setPixelRatio(window.devicePixelRatio);
  particle.renderer.setClearAlpha(0);
  container.appendChild(particle.renderer.domElement);

  window.addEventListener("resize", onWindowResize, {
    passive: false,
  });
  document.addEventListener("mousemove", onDocumentMouseMove, {
    passive: false,
  });
  document.addEventListener("touchstart", onDocumentTouchStart, {
    passive: false,
  });
  document.addEventListener("touchmove", onDocumentTouchMove, {
    passive: false,
  });
}

function render() {
  particle.camera.position.x += (particle.mouseX - particle.camera.position.x) * 0.05;
  particle.camera.position.y = 400;
  particle.camera.lookAt(particle.scene.position);
  const positions = particle.particles.geometry.attributes.position.array;
  const scales = particle.particles.geometry.attributes.scale.array;
  // 计算粒子位置及大小
  let i = 0;
  let j = 0;
  for (let ix = 0; ix < props.amountX; ix++) {
    for (let iy = 0; iy < props.amountY; iy++) {
      positions[i + 1] =
        Math.sin((ix + particle.count) * 0.3) * 100 +
        Math.sin((iy + particle.count) * 0.5) * 100;
      scales[j] =
        (Math.sin((ix + particle.count) * 0.3) + 1) * 16 +
        (Math.sin((iy + particle.count) * 0.5) + 1) * 16;
      i += 3;
      j++;
    }
  }
  // 重新渲染粒子
  particle.particles.geometry.attributes.position.needsUpdate = true;
  particle.particles.geometry.attributes.scale.needsUpdate = true;
  particle.renderer.render(particle.scene, particle.camera);
  particle.count += 0.1;
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

function onDocumentMouseMove(event) {
  particle.mouseX = event.clientX - particle.windowHalfX;
}

function onDocumentTouchStart(event) {
  if (event.touches.length === 1) {
    particle.mouseX = event.touches[0].pageX - particle.windowHalfX;
  }
}

function onDocumentTouchMove(event) {
  if (event.touches.length === 1) {
    event.preventDefault();
    particle.mouseX = event.touches[0].pageX - particle.windowHalfX;
  }
}

function onWindowResize() {
  console.log("onWindowResize");
  particle.windowHalfX = window.innerWidth / 2;
  particle.camera.aspect = window.innerWidth / window.innerHeight;
  particle.camera.updateProjectionMatrix();
  particle.renderer.setSize(window.innerWidth, window.innerHeight);
}

</script>

<style lang="scss" scoped>
.wrap {
  width: 100%;
  height: 100%;
  background-color: black;

  #threeParticle {
    overflow: hidden;
    width: 100%;
    height: 100%;
  }
}
</style>
 
